home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / sun-calendar-event-dialog-attendees.js < prev    next >
Text File  |  2007-12-03  |  30KB  |  892 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Sun Microsystems code.
  15.  *
  16.  * The Initial Developer of the Original Code is Sun Microsystems.
  17.  * Portions created by the Initial Developer are Copyright (C) 2006
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Michael Buettner <michael.buettner@sun.com>
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. var gStartDate = null;
  38. var gEndDate = null;
  39. var gStartTimezone = null;
  40. var gEndTimezone = null;
  41. var gDuration = null;
  42. var gStartHour = 0;
  43. var gEndHour = 24;
  44. var gIsReadOnly = false;
  45. var gIsInvitation = false;
  46. var gIgnoreUpdate = false;
  47. var gDisplayTimezone = true;
  48. var gUndoStack = [];
  49. var gForce24Hours = false;
  50. var gZoomFactor = 100;
  51.  
  52. function onLoad() {
  53.     // first of all, attach all event handlers
  54.     window.addEventListener("resize", onResize, true);
  55.     window.addEventListener("modify", onModify, true);
  56.     window.addEventListener("rowchange", onRowChange, true);
  57.     window.addEventListener("DOMMouseScroll", onMouseScroll, true);
  58.     window.addEventListener("DOMAttrModified", onAttrModified, true);
  59.     window.addEventListener("timebar", onTimebar, true);
  60.     window.addEventListener("timechange", onTimeChange, true);
  61.  
  62.     var args = window.arguments[0];
  63.     var startTime = args.startTime;
  64.     var endTime = args.endTime;
  65.     var calendar = args.calendar;
  66.  
  67.     gDisplayTimezone = args.displayTimezone;
  68.  
  69.     onChangeCalendar(calendar);
  70.  
  71.     // we need to enforce several layout constraints which can't be modelled
  72.     // with plain xul and css, at least as far as i know.
  73.     const kStylesheet = "chrome://calendar/content/sun-calendar-event-dialog.css";
  74.     for each (var stylesheet in document.styleSheets) {
  75.         if (stylesheet.href == kStylesheet) {
  76.             // make the dummy-spacer #1 [top] the same height as the timebar
  77.             var timebar = document.getElementById("timebar");
  78.             stylesheet.insertRule(
  79.                 ".attendee-spacer-top { height: "
  80.                     + timebar.boxObject.height+"px; }", 0);
  81.             // make the dummy-spacer #2 [bottom] the same height as the scrollbar
  82.             var scrollbar = document.getElementById("horizontal-scrollbar");
  83.             stylesheet.insertRule(
  84.                 ".attendee-spacer-bottom { height: "
  85.                     + scrollbar.boxObject.height+"px; }", 0);
  86.             break;
  87.         }
  88.     }
  89.  
  90.     var zoom = document.getElementById("zoom-menulist");
  91.     zoom.value = "100";
  92.  
  93.     initTimeRange();
  94.  
  95.     // Check if an all-day event has been passed in (to adapt endDate).
  96.     if (startTime.isDate) {
  97.         startTime = startTime.clone();
  98.         endTime = endTime.clone();
  99.  
  100.         endTime.day--;
  101.  
  102.         // for all-day events we expand to 24hrs, set zoom-factor to 25%
  103.         // and disable the zoom-control.
  104.         setForce24Hours(true);
  105.         zoom.value = "400";
  106.         zoom.setAttribute("disabled", "true");
  107.         setZoomFactor(zoom.value);
  108.     }
  109.  
  110.     loadDateTime(startTime, endTime);
  111.     propagateDateTime();
  112.  
  113.     updateButtons();
  114.  
  115.     // attach an observer to get notified of changes
  116.     // that are relevant to this dialog.
  117.     var prefObserver = {
  118.         observe: function aD_observe(aSubject, aTopic, aPrefName) {
  119.             switch (aPrefName) {
  120.                 case "calendar.view.daystarthour":
  121.                 case "calendar.view.dayendhour":
  122.                     initTimeRange();
  123.                     propagateDateTime();
  124.                     break;
  125.             }
  126.         }
  127.     }
  128.     var pb2 = Components.classes["@mozilla.org/preferences-service;1"].
  129.               getService(Components.interfaces.nsIPrefBranch2);
  130.     pb2.addObserver("calendar.", prefObserver, false);
  131.     window.addEventListener("unload",
  132.         function() {
  133.             pb2.removeObserver("calendar.", prefObserver);
  134.         },
  135.         false);
  136.  
  137.     opener.setCursor("auto");
  138.     self.focus();
  139. }
  140.  
  141. function onAccept() {
  142.     var attendees = document.getElementById("attendees-list");
  143.     window.arguments[0].onOk(
  144.         attendees.attendees,
  145.         attendees.organizer,
  146.         gStartDate.getInTimezone(gStartTimezone),
  147.         gEndDate.getInTimezone(gEndTimezone));
  148.     return true;
  149. }
  150.  
  151. function onCancel() {
  152.     return true;
  153. }
  154.  
  155. function onZoomFactor(aValue) {
  156.     setZoomFactor(parseInt(aValue));
  157. }
  158.  
  159. function loadDateTime(aStartDate, aEndDate) {
  160.     gDuration = aEndDate.subtractDate(aStartDate);
  161.     var kDefaultTimezone = calendarDefaultTimezone();
  162.     gStartTimezone = aStartDate.timezone;
  163.     gEndTimezone = aEndDate.timezone;
  164.     gStartDate = aStartDate.getInTimezone(kDefaultTimezone);
  165.     gEndDate = aEndDate.getInTimezone(kDefaultTimezone);
  166.     gStartDate.makeImmutable();
  167.     gEndDate.makeImmutable();
  168. }
  169.  
  170. function propagateDateTime() {
  171.     // Fill the controls
  172.     updateDateTime();
  173.  
  174.     // Tell the timebar about the new start/enddate
  175.     var timebar = document.getElementById("timebar");
  176.     timebar.startDate = gStartDate;
  177.     timebar.endDate = gEndDate;
  178.     timebar.refresh();
  179.  
  180.     // Tell the selection-bar about the new start/enddate
  181.     var selectionbar = document.getElementById("selection-bar");
  182.     selectionbar.startDate = gStartDate;
  183.     selectionbar.endDate = gEndDate;
  184.     selectionbar.update();
  185.  
  186.     // Tell the freebusy grid about the new start/enddate
  187.     var grid = document.getElementById("freebusy-grid");
  188.  
  189.     var refresh = (grid.startDate == null) ||
  190.                   (grid.startDate.compare(gStartDate) != 0) ||
  191.                   (grid.endDate == null) ||
  192.                   (grid.endDate.compare(gEndDate) != 0);
  193.     grid.startDate = gStartDate;
  194.     grid.endDate = gEndDate;
  195.     if (refresh) {
  196.         grid.forceRefresh();
  197.     }
  198.  
  199.     // Expand to 24hrs if the new range is outside of the default range.
  200.     var kDefaultTimezone = calendarDefaultTimezone();
  201.     var startTime = gStartDate.getInTimezone(kDefaultTimezone);
  202.     var endTime = gEndDate.getInTimezone(kDefaultTimezone);
  203.     if ((startTime.hour < gStartHour) ||
  204.         (endTime.hour > gEndHour) ||
  205.         (startTime.isDate)) {
  206.         setForce24Hours(true);
  207.     }
  208. }
  209.  
  210. /**
  211.  * assumes that gStartDate and gEndDate have been correctly initialized,
  212.  * either by having called loadDateTime() or having read the information
  213.  * from the controls due to recent user input. gStartDate and gEndDate are
  214.  * assumed to always be in the default timezone while the actual timezones
  215.  * are expected at gStartTimezone and gEndTimezone. this function attempts
  216.  * to copy these state related informations to the dialog controls, which
  217.  * makes it read from the variables and write to the controls (i.e. it
  218.  * doesn't change the state).
  219.  */
  220. function updateDateTime() {
  221.     // Convert to default timezone if the timezone option
  222.     // is *not* checked, otherwise keep the specific timezone
  223.     // and display the labels in order to modify the timezone.
  224.     if (gDisplayTimezone) {
  225.         var startTime = gStartDate.getInTimezone(gStartTimezone);
  226.         var endTime = gEndDate.getInTimezone(gEndTimezone);
  227.  
  228.         if (startTime.isDate) {
  229.             document.getElementById("all-day")
  230.                 .setAttribute("checked", "true");
  231.         }
  232.  
  233.         // In the case where the timezones are different but
  234.         // the timezone of the endtime is "UTC", we convert
  235.         // the endtime into the timezone of the starttime.
  236.         if (startTime && endTime) {
  237.             if (!compareObjects(startTime.timezone, endTime.timezone)) {
  238.                 if (endTime.timezone.isUTC) {
  239.                     endTime = endTime.getInTimezone(startTime.timezone);
  240.                 }
  241.             }
  242.         }
  243.  
  244.         // Before feeding the date/time value into the control we need
  245.         // to set the timezone to 'floating' in order to avoid the
  246.         // automatic conversion back into the OS timezone.
  247.         startTime.timezone = floating();
  248.         endTime.timezone = floating();
  249.  
  250.         document.getElementById("event-starttime").value = startTime.jsDate;
  251.         document.getElementById("event-endtime").value = endTime.jsDate;
  252.     } else {
  253.         var kDefaultTimezone = calendarDefaultTimezone();
  254.  
  255.         var startTime = gStartDate.getInTimezone(kDefaultTimezone);
  256.         var endTime = gEndDate.getInTimezone(kDefaultTimezone);
  257.  
  258.         if (startTime.isDate) {
  259.             document.getElementById("all-day")
  260.                 .setAttribute("checked", "true");
  261.         }
  262.  
  263.         // Before feeding the date/time value into the control we need
  264.         // to set the timezone to 'floating' in order to avoid the
  265.         // automatic conversion back into the OS timezone.
  266.         startTime.timezone = floating();
  267.         endTime.timezone = floating();
  268.  
  269.         document.getElementById("event-starttime").value = startTime.jsDate;
  270.         document.getElementById("event-endtime").value = endTime.jsDate;
  271.     }
  272.  
  273.     updateTimezone();
  274.     updateAllDay();
  275. }
  276.  
  277. function timezoneString(tz) {
  278.     var tzid = tz.tzid;
  279.     var prefix = getTimezoneService().tzidPrefix;
  280.     if (tzid.indexOf(prefix) == 0) {
  281.         tzid = tzid.substring(prefix.length);
  282.     }
  283.     return tzid;
  284. }
  285.  
  286. /**
  287.  * assumes that gStartDate and gEndDate have been correctly initialized,
  288.  * either by having called loadDateTime() or having read the information
  289.  * from the controls due to recent user input. gStartDate and gEndDate are
  290.  * assumed to always be in the default timezone while the actual timezones
  291.  * are expected at gStartTimezone and gEndTimezone. this function attempts
  292.  * to copy these state related informations to the dialog controls, which
  293.  * makes it read from the variables and write to the controls (i.e. it
  294.  * doesn't change the state).
  295.  */
  296. function updateTimezone() {
  297.     gIgnoreUpdate = true;
  298.  
  299.     if (gDisplayTimezone) {
  300.         var startTimezone = gStartTimezone;
  301.         var endTimezone = gEndTimezone;
  302.         var equalTimezones = false;
  303.         if (startTimezone && endTimezone &&
  304.             (compareObjects(startTimezone, endTimezone) || endTimezone.isUTC)) {
  305.             equalTimezones = true;
  306.         }
  307.  
  308.         var tzStart = document.getElementById("timezone-starttime");
  309.         var tzEnd = document.getElementById("timezone-endtime");
  310.         if (startTimezone != null) {
  311.             tzStart.removeAttribute('collapsed');
  312.             tzStart.value = timezoneString(startTimezone);
  313.         } else {
  314.             tzStart.setAttribute('collapsed', 'true');
  315.         }
  316.  
  317.         // we never display the second timezone if both are equal
  318.         if (endTimezone != null && !equalTimezones) {
  319.             tzEnd.removeAttribute('collapsed');
  320.             tzEnd.value = timezoneString(endTimezone);
  321.         } else {
  322.             tzEnd.setAttribute('collapsed', 'true');
  323.         }
  324.     } else {
  325.         document.getElementById("timezone-starttime")
  326.             .setAttribute('collapsed', 'true');
  327.         document.getElementById("timezone-endtime")
  328.             .setAttribute('collapsed', 'true');
  329.     }
  330.  
  331.     gIgnoreUpdate = false;
  332. }
  333.  
  334. function updateStartTime() {
  335.     if (gIgnoreUpdate) {
  336.         return;
  337.     }
  338.  
  339.     var startWidgetId = "event-starttime";
  340.     var endWidgetId = "event-endtime";
  341.  
  342.     var startWidget = document.getElementById(startWidgetId);
  343.     var endWidget = document.getElementById(endWidgetId);
  344.  
  345.     // jsDate is always in OS timezone, thus we create a calIDateTime
  346.     // object from the jsDate representation and simply set the new
  347.     // timezone instead of converting.
  348.     var start = jsDateToDateTime(startWidget.value,
  349.                                  gDisplayTimezone ? gStartTimezone : calendarDefaultTimezone());
  350.     gStartDate = start.clone();
  351.     start.addDuration(gDuration);
  352.     gEndDate = start.getInTimezone(gEndTimezone);
  353.  
  354.     var allDayElement = document.getElementById("all-day");
  355.     var allDay = allDayElement.getAttribute("checked") == "true";
  356.     if (allDay) {
  357.         gStartDate.isDate = true;
  358.         gEndDate.isDate = true;
  359.     }
  360.  
  361.     propagateDateTime();
  362. }
  363.  
  364. function updateEndTime() {
  365.     if (gIgnoreUpdate) {
  366.         return;
  367.     }
  368.  
  369.     var startWidgetId = "event-starttime";
  370.     var endWidgetId = "event-endtime";
  371.  
  372.     var startWidget = document.getElementById(startWidgetId);
  373.     var endWidget = document.getElementById(endWidgetId);
  374.  
  375.     var saveStartTime = gStartDate;
  376.     var saveEndTime = gEndDate;
  377.     var kDefaultTimezone = calendarDefaultTimezone();
  378.  
  379.     gStartDate = jsDateToDateTime(startWidget.value,
  380.                                   gDisplayTimezone ? gStartTimezone : calendarDefaultTimezone());
  381.  
  382.     var timezone = gEndTimezone;
  383.     if (timezone.isUTC &&
  384.         gStartDate &&
  385.         !compareObjects(gStartTimezone, gEndTimezone)) {
  386.         timezone = gStartTimezone;
  387.     }
  388.     gEndDate = jsDateToDateTime(endWidget.value,
  389.                                 gDisplayTimezone ? timezone : kDefaultTimezone);
  390.  
  391.     var allDayElement = document.getElementById("all-day");
  392.     var allDay = allDayElement.getAttribute("checked") == "true";
  393.     if (allDay) {
  394.         gStartDate.isDate = true;
  395.         gEndDate.isDate = true;
  396.     }
  397.  
  398.     // Calculate the new duration of start/end-time.
  399.     // don't allow for negative durations.
  400.     var warning = false;
  401.     if (gEndDate.compare(gStartDate) >= 0) {
  402.         gDuration = gEndDate.subtractDate(gStartDate);
  403.     } else {
  404.         gStartDate = saveStartTime;
  405.         gEndDate = saveEndTime;
  406.         warning = true;
  407.     }
  408.  
  409.     propagateDateTime();
  410.  
  411.     if (warning) {
  412.         var callback = function() {
  413.             var promptService =
  414.                 Components.classes[
  415.                     "@mozilla.org/embedcomp/prompt-service;1"]
  416.                     .getService(
  417.                         Components.interfaces.nsIPromptService);
  418.             promptService.alert(
  419.                 null,
  420.                 document.title,
  421.                 calGetString("calendar", "warningNegativeDuration"));
  422.         }
  423.         setTimeout(callback, 1);
  424.     }
  425. }
  426.  
  427. function editStartTimezone() {
  428.     var tzStart = document.getElementById("timezone-starttime");
  429.     if (tzStart.hasAttribute("disabled")) {
  430.         return;
  431.     }
  432.  
  433.     var self = this;
  434.     var args = new Object();
  435.     args.time = gStartDate.getInTimezone(gStartTimezone);
  436.     args.onOk = function(datetime) {
  437.         var equalTimezones = false;
  438.         if (gStartTimezone && gEndTimezone &&
  439.             compareObjects(gStartTimezone, gEndTimezone)) {
  440.             equalTimezones = true;
  441.         }
  442.         gStartTimezone = datetime.timezone;
  443.         if (equalTimezones) {
  444.             gEndTimezone = datetime.timezone;
  445.         }
  446.         self.propagateDateTime();
  447.     };
  448.  
  449.     // Open the dialog modally
  450.     openDialog(
  451.         "chrome://calendar/content/sun-calendar-event-dialog-timezone.xul",
  452.         "_blank",
  453.         "chrome,titlebar,modal,resizable",
  454.         args);
  455. }
  456.  
  457. function editEndTimezone() {
  458.     var tzStart = document.getElementById("timezone-endtime");
  459.     if (tzStart.hasAttribute("disabled")) {
  460.         return;
  461.     }
  462.  
  463.     var self = this;
  464.     var args = new Object();
  465.     args.time = gEndTime.getInTimezone(gEndTimezone);
  466.     args.onOk = function(datetime) {
  467.         if (gStartTimezone && gEndTimezone &&
  468.             compareObjects(gStartTimezone, gEndTimezone)) {
  469.             gStartTimezone = datetime.timezone;
  470.         }
  471.         gEndTimezone = datetime.timezone;
  472.         self.propagateDateTime();
  473.     };
  474.  
  475.     // Open the dialog modally
  476.     openDialog(
  477.         "chrome://calendar/content/sun-calendar-event-dialog-timezone.xul",
  478.         "_blank",
  479.         "chrome,titlebar,modal,resizable",
  480.         args);
  481. }
  482.  
  483. function updateAllDay() {
  484.     if (gIgnoreUpdate) {
  485.         return;
  486.     }
  487.  
  488.     var allDayElement = document.getElementById("all-day");
  489.     var allDay  = (allDayElement.getAttribute("checked") == "true");
  490.     var startpicker = document.getElementById("event-starttime");
  491.     var endpicker = document.getElementById("event-endtime");
  492.  
  493.     var tzStart = document.getElementById("timezone-starttime");
  494.     var tzEnd = document.getElementById("timezone-endtime");
  495.  
  496.     // Disable the timezone links if 'allday' is checked OR the
  497.     // calendar of this item is read-only. In any other case we
  498.     // enable the links.
  499.     if (allDay) {
  500.         startpicker.setAttribute("timepickerdisabled", "true");
  501.         endpicker.setAttribute("timepickerdisabled", "true");
  502.  
  503.         tzStart.setAttribute("disabled", "true");
  504.         tzEnd.setAttribute("disabled", "true");
  505.         tzStart.removeAttribute("class");
  506.         tzEnd.removeAttribute("class");
  507.     } else {
  508.         startpicker.removeAttribute("timepickerdisabled");
  509.         endpicker.removeAttribute("timepickerdisabled");
  510.  
  511.         tzStart.removeAttribute("disabled");
  512.         tzEnd.removeAttribute("disabled");
  513.         tzStart.setAttribute("class", "text-link");
  514.         tzEnd.setAttribute("class", "text-link");
  515.     }
  516. }
  517.  
  518. function changeAllDay() {
  519.     var allDayElement = document.getElementById("all-day");
  520.     var allDay = (allDayElement.getAttribute("checked") == "true");
  521.  
  522.     gStartDate = gStartDate.clone();
  523.     gEndDate = gEndDate.clone();
  524.  
  525.     gStartDate.isDate = allDay;
  526.     gEndDate.isDate = allDay;
  527.  
  528.     propagateDateTime();
  529.  
  530.     // After propagating the modified times we enforce some constraints
  531.     // on the zoom-factor. in case this events is now said to be all-day,
  532.     // we automatically enforce a 25% zoom-factor and disable the control.
  533.     var zoom = document.getElementById("zoom-menulist");
  534.     if (allDay) {
  535.         zoom.value = "400";
  536.         zoom.setAttribute("disabled", "true");
  537.         setZoomFactor(zoom.value);
  538.         setForce24Hours(true);
  539.     } else {
  540.         zoom.removeAttribute("disabled");
  541.     }
  542. }
  543.  
  544. function onResize() {
  545.     // Don't do anything if we haven't been initialized.
  546.     if (!gStartDate || !gEndDate) {
  547.         return;
  548.     }
  549.  
  550.     var grid = document.getElementById("freebusy-grid");
  551.     var gridScrollbar = document.getElementById("horizontal-scrollbar");
  552.     grid.fitDummyRows();
  553.     var ratio = grid.boxObject.width / grid.documentSize;
  554.     var maxpos = gridScrollbar.getAttribute("maxpos");
  555.     var inc = maxpos * ratio / (1 - ratio);
  556.     gridScrollbar.setAttribute("pageincrement", inc);
  557.  
  558.     var attendees = document.getElementById("attendees-list");
  559.     var attendeesScrollbar = document.getElementById("vertical-scrollbar");
  560.     var box = document.getElementById("vertical-scrollbar-box");
  561.     attendees.fitDummyRows();
  562.     var ratio = attendees.boxObject.height / attendees.documentSize;
  563.     if (ratio < 1) {
  564.         box.removeAttribute("collapsed");
  565.         var maxpos = attendeesScrollbar.getAttribute("maxpos");
  566.         var inc = maxpos * ratio / (1 - ratio);
  567.         attendeesScrollbar.setAttribute("pageincrement", inc);
  568.     } else {
  569.         box.setAttribute("collapsed", "true");
  570.     }
  571. }
  572.  
  573. function onChangeCalendar(calendar) {
  574.     var args = window.arguments[0];
  575.     var organizer = args.organizer;
  576.  
  577.     // set 'mIsReadOnly' if the calendar is read-only
  578.     if (calendar && calendar.readOnly) {
  579.         gIsReadOnly = true;
  580.     }
  581.  
  582.     // assume we're the organizer [in case that the calendar
  583.     // does not support the concept of identities].
  584.     gIsInvitation = false;
  585.  
  586.     try {
  587.         gIsInvitation = provider.isInvitation(args.item);
  588.     }
  589.     catch (e) {
  590.     }
  591.  
  592.     if (gIsReadOnly || gIsInvitation) {
  593.         document.getElementById("next-slot")
  594.             .setAttribute('disabled', 'true');
  595.         document.getElementById("previous-slot")
  596.             .setAttribute('disabled', 'true');
  597.     }
  598.  
  599.     var freebusy = document.getElementById("freebusy-grid");
  600.     freebusy.onChangeCalendar(calendar);
  601. }
  602.  
  603. function updateButtons() {
  604.     var previousButton = document.getElementById("previous-slot");
  605.     if (gUndoStack.length > 0) {
  606.         previousButton.removeAttribute('disabled');
  607.     } else {
  608.         previousButton.setAttribute('disabled', 'true');
  609.     }
  610. }
  611.  
  612. function onNextSlot() {
  613.     // Store the current setting in the undo-stack.
  614.     var currentSlot = {};
  615.     currentSlot.startTime = gStartDate;
  616.     currentSlot.endTime = gEndDate;
  617.     gUndoStack.push(currentSlot);
  618.  
  619.     // Ask the grid for the next possible timeslot.
  620.     var grid = document.getElementById("freebusy-grid");
  621.     var duration = gEndDate.subtractDate(gStartDate);
  622.     var start = grid.nextSlot();
  623.     var end = start.clone();
  624.     end.addDuration(duration);
  625.     if (start.isDate) {
  626.         end.day++;
  627.     }
  628.     gStartDate = start.clone();
  629.     gEndDate = end.clone();
  630.     var endDate = gEndDate.clone();
  631.  
  632.     // Check if an all-day event has been passed in (to adapt endDate).
  633.     if (gStartDate.isDate) {
  634.         gEndDate.day--;
  635.     }
  636.     gStartDate.makeImmutable();
  637.     gEndDate.makeImmutable();
  638.     endDate.makeImmutable();
  639.  
  640.     propagateDateTime();
  641.  
  642.     // Scroll the grid/timebar such that the current time is visible
  643.     scrollToCurrentTime();
  644.  
  645.     updateButtons();
  646. }
  647.  
  648. function onPreviousSlot() {
  649.     var previousSlot = gUndoStack.pop();
  650.     if (!previousSlot) {
  651.         return;
  652.     }
  653.  
  654.     // In case the new starttime happens to be scheduled
  655.     // on a different day, we also need to update the
  656.     // complete freebusy informations and appropriate
  657.     // underlying arrays holding the information.
  658.     var refresh = previousSlot.startTime.day != gStartDate.day;
  659.  
  660.     gStartDate = previousSlot.startTime.clone();
  661.     gEndDate = previousSlot.endTime.clone();
  662.     var endDate = gEndDate.clone();
  663.  
  664.     propagateDateTime();
  665.  
  666.     // scroll the grid/timebar such that the current time is visible
  667.     scrollToCurrentTime();
  668.  
  669.     updateButtons();
  670.  
  671.     if (refresh) {
  672.         var grid = document.getElementById("freebusy-grid");
  673.         grid.forceRefresh();
  674.     }
  675. }
  676.  
  677. function onMinus() {
  678.     var timebar = document.getElementById("timebar");
  679.     var ratio = timebar.scroll;
  680.     ratio -= timebar.step;
  681.     if (ratio <= 0.0) {
  682.         ratio = 0.0;
  683.     }
  684.     var scrollbar = document.getElementById("horizontal-scrollbar");
  685.     var maxpos = scrollbar.getAttribute("maxpos");
  686.     scrollbar.setAttribute("curpos", ratio * maxpos);
  687. }
  688.  
  689. function onPlus() {
  690.     var timebar = document.getElementById("timebar");
  691.     var ratio = timebar.scroll;
  692.     ratio += timebar.step;
  693.     if (ratio >= 1.0) {
  694.         ratio = 1.0;
  695.     }
  696.     var scrollbar = document.getElementById("horizontal-scrollbar");
  697.     var maxpos = scrollbar.getAttribute("maxpos");
  698.     scrollbar.setAttribute("curpos", ratio * maxpos);
  699. }
  700.  
  701. function scrollToCurrentTime() {
  702.     var timebar = document.getElementById("timebar");
  703.     var ratio = (gStartDate.hour - gStartHour) * timebar.step;
  704.     if (ratio <= 0.0) {
  705.         ratio = 0.0;
  706.     }
  707.     if (ratio >= 1.0) {
  708.         ratio = 1.0;
  709.     }
  710.     var scrollbar = document.getElementById("horizontal-scrollbar");
  711.     var maxpos = scrollbar.getAttribute("maxpos");
  712.     scrollbar.setAttribute("curpos", ratio * maxpos);
  713. }
  714.  
  715.  
  716. function setZoomFactor(aValue) {
  717.     if (gZoomFactor == aValue) {
  718.         return aValue;
  719.     }
  720.  
  721.     gZoomFactor = aValue;
  722.     var timebar = document.getElementById("timebar");
  723.     timebar.zoomFactor = gZoomFactor;
  724.     var selectionbar = document.getElementById("selection-bar");
  725.     selectionbar.zoomFactor = gZoomFactor;
  726.     var grid = document.getElementById("freebusy-grid");
  727.     grid.zoomFactor = gZoomFactor;
  728.  
  729.     // Calling onResize() will update the scrollbars and everything else
  730.     // that needs to adopt the previously made changes. We need to call
  731.     // this after the changes have actually been made...
  732.     onResize();
  733.  
  734.     var scrollbar = document.getElementById("horizontal-scrollbar");
  735.     if (scrollbar.hasAttribute("maxpos")) {
  736.         var curpos = scrollbar.getAttribute("curpos");
  737.         var maxpos = scrollbar.getAttribute("maxpos");
  738.         var ratio = curpos / maxpos;
  739.         timebar.scroll = ratio;
  740.         grid.scroll = ratio;
  741.         selectionbar.ratio = ratio;
  742.     }
  743.  
  744.     return aValue;
  745. }
  746.  
  747. function setForce24Hours(aValue) {
  748.     if (gForce24Hours == aValue) {
  749.       return aValue;
  750.     }
  751.  
  752.     gForce24Hours = aValue;
  753.     initTimeRange();
  754.     var timebar = document.getElementById("timebar");
  755.     timebar.force24Hours = gForce24Hours;
  756.     var selectionbar = document.getElementById("selection-bar");
  757.     selectionbar.force24Hours = gForce24Hours;
  758.     var grid = document.getElementById("freebusy-grid");
  759.     grid.force24Hours = gForce24Hours;
  760.  
  761.     // Calling onResize() will update the scrollbars and everything else
  762.     // that needs to adopt the previously made changes. We need to call
  763.     // this after the changes have actually been made...
  764.     onResize();
  765.  
  766.     var scrollbar = document.getElementById("horizontal-scrollbar");
  767.     if (!scrollbar.hasAttribute("maxpos")) {
  768.         return aValue;
  769.     }
  770.     var curpos = scrollbar.getAttribute("curpos");
  771.     var maxpos = scrollbar.getAttribute("maxpos");
  772.     var ratio = curpos / maxpos;
  773.     timebar.scroll = ratio;
  774.     grid.scroll = ratio;
  775.     selectionbar.ratio = ratio;
  776.  
  777.     return aValue;
  778. }
  779.  
  780. function initTimeRange() {
  781.     if (gForce24Hours) {
  782.         gStartHour = 0;
  783.         gEndHour = 24;
  784.     } else {
  785.         gStartHour = getPrefSafe("calendar.view.daystarthour", 8);
  786.         gEndHour = getPrefSafe("calendar.view.dayendhour", 19);
  787.     }
  788. }
  789.  
  790. function onModify(event) {
  791.     onResize();
  792.     document.getElementById(
  793.         "freebusy-grid")
  794.             .onModify(event);
  795. }
  796.  
  797. function onRowChange(event) {
  798.     var scrollbar = document.getElementById("vertical-scrollbar");
  799.     var attendees = document.getElementById("attendees-list");
  800.     var maxpos = scrollbar.getAttribute("maxpos");
  801.     scrollbar.setAttribute(
  802.         "curpos",
  803.         event.details / attendees.mMaxAttendees * maxpos);
  804. }
  805.  
  806. function onMouseScroll(event) {
  807.     // ignore mouse scrolling for now...
  808.     event.stopPropagation();
  809. }
  810.  
  811. function onAttrModified(event) {
  812.     if (event.attrName == "width") {
  813.         var selectionbar = document.getElementById("selection-bar");
  814.         selectionbar.setWidth(selectionbar.boxObject.width);
  815.         return;
  816.     }
  817.  
  818.     // Synchronize grid and attendee list
  819.     var target = event.originalTarget;
  820.     if (target.hasAttribute("anonid") &&
  821.         target.getAttribute("anonid") == "input" &&
  822.         event.attrName == "focused" &&
  823.         event.newValue == "true") {
  824.         var attendees = document.getElementById("attendees-list");
  825.         var grid = document.getElementById("freebusy-grid");
  826.         if (grid.firstVisibleRow != attendees.firstVisibleRow) {
  827.             grid.firstVisibleRow = attendees.firstVisibleRow;
  828.         }
  829.     }
  830.  
  831.     if (event.originalTarget.localName == "scrollbar") {
  832.         var scrollbar = event.originalTarget;
  833.         if (scrollbar.hasAttribute("maxpos")) {
  834.             if (scrollbar.getAttribute("id") == "vertical-scrollbar") {
  835.                 var attendees = document.getElementById("attendees-list");
  836.                 var grid = document.getElementById("freebusy-grid");
  837.                 if (event.attrName == "curpos") {
  838.                     var maxpos = scrollbar.getAttribute("maxpos");
  839.                     attendees.ratio = event.newValue / maxpos;
  840.                 }
  841.                 grid.firstVisibleRow = attendees.firstVisibleRow;
  842.             } else if (scrollbar.getAttribute("id") == "horizontal-scrollbar") {
  843.                 if (event.attrName == "curpos") {
  844.                     var maxpos = scrollbar.getAttribute("maxpos");
  845.                     var ratio = event.newValue/maxpos;
  846.                     var timebar = document.getElementById("timebar");
  847.                     var grid = document.getElementById("freebusy-grid");
  848.                     var selectionbar = document.getElementById("selection-bar");
  849.                     timebar.scroll = ratio;
  850.                     grid.scroll = ratio;
  851.                     selectionbar.ratio = ratio;
  852.                 }
  853.             }
  854.         }
  855.     }
  856. }
  857.  
  858. function onTimebar(event) {
  859.     document.getElementById(
  860.         "selection-bar")
  861.             .init(event.details, event.height);
  862. }
  863.  
  864. function onTimeChange(event) {
  865.     var start = event.startDate.getInTimezone(gStartTimezone);
  866.     var end = event.endDate.getInTimezone(gEndTimezone);
  867.  
  868.     loadDateTime(start, end);
  869.  
  870.     // fill the controls
  871.     updateDateTime();
  872.  
  873.     // tell the timebar about the new start/enddate
  874.     var timebar = document.getElementById("timebar");
  875.     timebar.startDate = gStartDate;
  876.     timebar.endDate = gEndDate;
  877.     timebar.refresh();
  878.  
  879.     // tell the freebusy grid about the new start/enddate
  880.     var grid = document.getElementById("freebusy-grid");
  881.  
  882.     var refresh = (grid.startDate == null) ||
  883.                   (grid.startDate.compare(gStartDate) != 0) ||
  884.                   (grid.endDate == null) ||
  885.                   (grid.endDate.compare(gEndDate) != 0);
  886.     grid.startDate = gStartDate;
  887.     grid.endDate = gEndDate;
  888.     if (refresh) {
  889.         grid.forceRefresh();
  890.     }
  891. }
  892.